﻿// <copyright file="TrackSMFPlayer.cs" company="Microsoft">
// Copyright (c) 2010 Microsoft All Rights Reserved
// </copyright>
// <author>Microsoft</author>
// <email>mischero@microsoft.com</email>
// <date>2010-05-21</date>
// <summary>Web analytics service behavior class definition</summary>

namespace Microsoft.WebAnalytics.Media
{
    using System;
    using System.Collections.ObjectModel;
    using System.ComponentModel.Composition;
    using System.Globalization;
    using System.Linq;
    using System.Windows;
    using System.Windows.Interactivity;
    using System.Windows.Media;
    using Microsoft.SilverlightMediaFramework.Core;
    using Microsoft.SilverlightMediaFramework.Plugins.Primitives;
    using Microsoft.WebAnalytics;
    using Microsoft.WebAnalytics.Data;

    /// <summary>
    /// <img src="../media/Microsoft.WebAnalytics.Media.TrackPlayer.24x24.png"/> 
    /// Track Microsoft Silvelright Media Framework Player 2.0 Events
    /// </summary>
    /// <remarks>
    /// <para>
    /// Use the SMFPlayer.LogLevel to determine the level of logging that that this tracker will support.
    /// </para>
    /// <list type="table">
    /// <listheader>
    /// <term>AnalyticsEvent Property</term>
    /// <term>Description</term>
    /// </listheader>
    /// <item>
    /// <term>ActionValue</term>
    /// <description>The message or event-specific data.  See the How-To Topic <a href="b512e1dc-e693-450c-95de-c61f1edfc63b.htm">Silverlight Media Framework Events</a> for more details.
    /// </description>
    /// </item>
    /// <item>
    /// <term>Category</term>
    /// <description>The severity of the message, from LogEntry.Severity.</description>
    /// </item>
    /// <item>
    /// <term>Clip.CurrentClipState</term>
    /// <description>the Current Clip state</description>
    /// </item>
    /// <item>
    /// <term>ContentId</term>
    /// <description>the URL of the media file</description>
    /// </item>
    /// <item>
    /// <term>EventArgs</term>
    /// <description>the TrackSMFPlayer.CustomProperties, CurrentClipState, Severity, VolumeLevel (0-100)</description>
    /// </item>
    /// <item>
    /// <term>MediaSource</term>
    /// <description>the URL of the media</description>
    /// </item>
    /// <item>
    /// <term>MediaTimeCode</term>
    /// <description>The time code in the media</description>
    /// </item>
    /// <item>
    /// <term>Name</term>
    /// <description>The name of the event</description>
    /// </item>
    /// <item>
    /// <term>ObjectName</term>
    /// <description>the name of the SMFPlayer</description>
    /// </item>
    /// <item>
    /// <term>ObjectType</term>
    /// <description>SMFPlayer</description>
    /// </item>
    /// <item>
    /// <term>Value</term>
    /// <description>The message or event-specific data.  See the How-To Topic <a href="b512e1dc-e693-450c-95de-c61f1edfc63b.htm">Silverlight Media Framework Events</a> for more details.
    /// </description>
    /// </item>
    /// </list>
    /// Include References:
    /// <list type="bullet">
    /// <item>
    /// <description>Microsoft.SilverlightMediaFramework.Core</description>
    /// </item>
    /// <item>
    /// <description>Microsoft.SilverlightMediaFramework.Plugins</description>
    /// </item>
    /// <item>
    /// <description>Microsoft.SilverlightMediaFramework.Utilities</description>
    /// </item>
    /// </list>
    /// </remarks>
    public class TrackSMFPlayer : Behavior<SMFPlayer>
    {
        #region Constructors
        /// <summary>
        /// Initializes a new instance of the TrackSMFPlayer class.
        /// </summary>
        public TrackSMFPlayer()
        {
            this.CustomProperties = new Collection<PropertyValue>();
        }

        #endregion

        #region Properties

        /// <summary>
        /// Gets custom properties (name/value pairs) for the action
        /// </summary>
        public Collection<PropertyValue> CustomProperties { get; private set; }

        /// <summary>
        /// Gets or sets the event Logging method
        /// </summary>
        [Import("Log")]
        public Action<AnalyticsEvent> Log { get; set; }

        /// <summary>
        /// Gets the current MediaElement State
        /// </summary>
        public System.Windows.Media.MediaElementState CurrentClipState
        {
            get
            {
                try
                {
                    int state = (int)this.AssociatedObject.PlayState;

                    return (MediaElementState)Enum.ToObject(typeof(MediaElementState), state);
                }
                catch (System.Exception se)
                {
                    System.Diagnostics.Debug.WriteLine(se.Message);

                    return MediaElementState.Closed;
                }
            }
        }

        #endregion

        #region Methods
        /// <summary>
        /// Attach events and initialize parts
        /// </summary>
        protected override void OnAttached()
        {
            base.OnAttached();

            CompositionInitializerEx.SatisfyImports(this);

            this.AssociatedObject.LogEntryReceived += new EventHandler<CustomEventArgs<LogEntry>>(this.AssociatedObject_LogEntryReceived);

            this.AssociatedObject.Unloaded += new RoutedEventHandler(delegate(object sender, RoutedEventArgs args)
            {
                System.Windows.Interactivity.Interaction.GetBehaviors(this.AssociatedObject).Remove(this);
            });
        }

        /// <summary>
        /// Detach events
        /// </summary>
        protected override void OnDetaching()
        {
            base.OnDetaching();

            this.AssociatedObject.LogEntryReceived -= new EventHandler<CustomEventArgs<LogEntry>>(this.AssociatedObject_LogEntryReceived);
        }

        #endregion

        #region Implementation
        /// <summary>
        /// Add parameters for specific events
        /// </summary>
        /// <param name="arg2">the log entry</param>
        /// <param name="logEvent">the log event</param>
        private void AddEventParameters(LogEntry arg2, AnalyticsEvent logEvent)
        {
            switch (arg2.Type)
            {
                case "PluginLoaded":
                    break;

                case "PlaylistChanged":
                    break;

                case "MaximumPlaybackBitrateChanged":
                    logEvent.Value = System.Convert.ToInt32(arg2.ExtendedProperties["Bitrate"]);
                    break;

                case "PlaylistItemChanged":
                    break;

                case "PlayStateChanged":
                    logEvent.ActionValue = arg2.ExtendedProperties["PlayState"].ToString();
                    break;

                case "ConfiguredVideoSize":
                    logEvent.ActionValue = string.Format(CultureInfo.InvariantCulture, "{0},{1}", arg2.ExtendedProperties["Width"], arg2.ExtendedProperties["Height"]);
                    break;

                case "GenericErrorOccurred":
                    break;

                case "VolumeLevelChanged":
                    logEvent.Value = System.Convert.ToInt32((100 * (double)arg2.ExtendedProperties["VolumeLevel"]) + 0.5);
                    break;

                case "ManifestReady":
                    break;

                case "VideoDownloadBitrateChanged":
                    logEvent.Value = System.Convert.ToInt32(arg2.ExtendedProperties["Bitrate"]);
                    break;

                case "MediaOpened":
                    logEvent.ActionValue = logEvent.ContentId;
                    break;

                case "DownloadProgressChanged":
                    logEvent.Value = 100 * System.Convert.ToInt32(arg2.ExtendedProperties["DownloadProgress"]);
                    break;

                case "AudioStreamChanged":
                    logEvent.ActionValue = arg2.ExtendedProperties["StreamName"] == null ? null : arg2.ExtendedProperties["StreamName"].ToString();
                    break;

                case "VideoPlaybackBitrateChanged":
                    logEvent.Value = System.Convert.ToInt32(arg2.ExtendedProperties["Bitrate"]);
                    break;

                case "SeekToPosition":
                    break;

                case "ScrubbingStarted":
                    logEvent.ActionValue = arg2.ExtendedProperties["NewPosition"].ToString();
                    break;

                case "ScrubbingCompleted":
                    logEvent.ActionValue = arg2.ExtendedProperties["NewPosition"].ToString();
                    break;

                case "PlaybackRateChanged":
                    logEvent.Value = 100 * System.Convert.ToInt32(arg2.ExtendedProperties["PlaybackRate"]);
                    break;

                case "FullScreenChanged":
                    logEvent.Value = System.Convert.ToInt32(arg2.ExtendedProperties["IsFullScreen"]);
                    break;

                case "PlaySpeedStateChanged":
                    logEvent.ActionValue = arg2.ExtendedProperties["PlaySpeedState"].ToString();
                    break;

                case "IsMutedChanged":
                    logEvent.Value = System.Convert.ToInt32(arg2.ExtendedProperties["IsMuted"]);
                    break;

                default:
                    if (arg2.ExtendedProperties.Count > 0)
                    {
                        var extendedProperties = from item in arg2.ExtendedProperties
                                                 select string.Format(CultureInfo.InvariantCulture, "{0}:{1}", item.Key, item.Value);

                        var stringParameters = string.Join(",", extendedProperties.ToArray());

                        System.Diagnostics.Debug.WriteLine("{0}: {1}", arg2.Type, stringParameters);
                    }

                    break;
            }
        }

        /// <summary>
        /// Track the LogEntryReceived event
        /// </summary>
        /// <param name="sender">the SMF player</param>
        /// <param name="e">the log entry</param>
        private void AssociatedObject_LogEntryReceived(object sender, CustomEventArgs<LogEntry> e)
        {
            if (string.IsNullOrEmpty(e.Value.Type))
            {
                System.Diagnostics.Debug.WriteLine("TrackSMFPlayer: No LogEntry.Type for {0}.", e.Value.Message);

                return;
            }

            var mediaUrl = this.AssociatedObject.CurrentPlaylistItem == null ? null : this.AssociatedObject.CurrentPlaylistItem.MediaSource;

            bool isSmoothStreaming = false;

            if (this.AssociatedObject.CurrentPlaylistItem != null)
            {
                isSmoothStreaming = this.AssociatedObject.CurrentPlaylistItem.DeliveryMethod == DeliveryMethods.AdaptiveStreaming;
            }

            // Initialize the AnalyticsEvent with parameters specific to the control event
            var logEvent = new AnalyticsEvent()
            {
                ActionValue = e.Value.Message,
                Category = e.Value.Severity.ToString(),
                Clip = new ClipContext
                {
                    ClipInformation = new ClipInformation()
                    {
                        ClipUrl = mediaUrl,
                        Duration = this.AssociatedObject.EndPosition,
                        IsSmoothStreamingSource = isSmoothStreaming
                    },
                    CurrentClipState = this.CurrentClipState,
                },
                ContentId = mediaUrl == null ? null : mediaUrl.ToString(),
                EventArgs = new Collection<PropertyValue>(),
                MediaSource = mediaUrl,
                MediaTimeCode = this.AssociatedObject.PlaybackPosition,
                Name = e.Value.Type,
                ObjectName = this.AssociatedObject.Name,
                ObjectType = this.AssociatedObject.GetType().Name
            };

            foreach (var pv in this.CustomProperties)
            {
                logEvent.EventArgs.Add(pv);
            }

            logEvent.EventArgs.Add(new PropertyValue { PropertyName = "CurrentClipState", Value = this.AssociatedObject.PlayState.ToString() });
            logEvent.EventArgs.Add(new PropertyValue { PropertyName = "Severity", Value = e.Value.Severity.ToString() });
            var volumeLevel = Math.Floor((this.AssociatedObject.VolumeLevel * 100) + 0.5).ToString();
            logEvent.EventArgs.Add(new PropertyValue { PropertyName = "VolumeLevel", Value = volumeLevel });

            this.AddEventParameters(e.Value, logEvent);

            // Log the event
            this.Log(logEvent);
        }

        #endregion
    }
}
